﻿namespace Game
{
    public class Engine
    {
        private Board m_Board;
        private Player[] m_Players;

        public enum eInputType 
        { 
            Quit,
            Move, 
            Invalid 
        }

        private eInputType turn(int i_currentPlayerIndex, out bool o_Success)
        {
            const int k_NumOfCardsToReveal = 2;
            Board.Coordinate[] moves = new Board.Coordinate[k_NumOfCardsToReveal];
            eInputType inputType = eInputType.Invalid;
            bool isFirstMoveInTurn;

            ConsoleIO.DrawBoard(this); 
            int movesIndex = 0;
            while ((movesIndex < k_NumOfCardsToReveal) && (inputType != eInputType.Quit))
            {
                isFirstMoveInTurn = movesIndex == 0;
                inputType = m_Players[i_currentPlayerIndex].MakeMove(m_Board, out moves[movesIndex], isFirstMoveInTurn);
                if (inputType == eInputType.Move)
                {
                    m_Board.FlipCard(moves[movesIndex]);
                    ConsoleIO.DrawBoard(this);

                    AI.RegisterCardForAIPlayers(moves[movesIndex], m_Board.GetCard(moves[movesIndex]), m_Board, m_Players);
                }

                movesIndex++;
            }

            foreach (Board.Coordinate currentCoordinate in moves)
            {
                 m_Board.FlipCard(currentCoordinate);
            }

            o_Success = false;
            if (inputType != eInputType.Quit)
            {
                if (m_Board.GetCard(moves[0]).Equals(m_Board.GetCard(moves[1])))
                {
                    foreach (Board.Coordinate currentMove in moves)
                    {
                        m_Board.BurnCard(currentMove);
                    }

                    o_Success = true;
                }
            }

            return inputType;              
        }

        public eInputType Match()
        {
            bool success;
            int currentPlayer = 0;
            eInputType inputType = eInputType.Invalid;

            AI.ResetAI(m_Players);

            while (inputType != eInputType.Quit && !checkEndGame())
            {
                inputType = turn(currentPlayer, out success);

                if (success == false)
                {
                    currentPlayer = (currentPlayer + 1) % m_Players.Length;
                }
                else
                {
                    m_Players[currentPlayer].Score++;
                }

                if (checkEndGame() == false && (inputType != eInputType.Quit))
                {
                    ConsoleIO.EndOfTurn(success, m_Players[currentPlayer]);
                }
            }

            if (inputType == eInputType.Move)
            {
                ConsoleIO.EndOfMatch(m_Players);
            }

            return inputType;
        }

        private bool checkEndGame()
        {
            return m_Board.IsAllBoardUpturned();
        }

        public Player Player1
        {
            get { return m_Players[0]; }
            private set { m_Players[0] = value; }
        }

        public Player Player2
        {
            get { return m_Players[1]; }
            private set { m_Players[1] = value; }
        }

        public Board Board
        {
            get { return m_Board; }
            private set { m_Board = value; }
        }

        public struct GameSettings
        {
            private int m_Rows;
            private int m_Cols;
            private bool m_IsSinglePlayer;
            private string m_Player1Name;
            private string m_Player2Name;

            public int Rows
            {
                get { return m_Rows; }
                set { m_Rows = value; }
            }

            public int Cols
            {
                get { return m_Cols; }
                set { m_Cols = value; }
            }

            public bool IsSinglePlayer
            {
                get { return m_IsSinglePlayer; }
                set { m_IsSinglePlayer = value; }
            }

            public string Player1Name
            {
                get { return m_Player1Name; }
                set { m_Player1Name = value; }
            }

            public string Player2Name
            {
                get { return m_Player2Name; }
                set { m_Player2Name = value; }
            }
        }

        public Engine(GameSettings i_Settings)
        {
            bool isBoardValid = Board.Make(i_Settings.Rows, i_Settings.Cols, out m_Board);
            if (!isBoardValid) 
            {
                throw new System.Exception("Exception: Bad board parameters to Game c'tor.\n");
            }

            m_Players = new Player[2];
            Player.ePlayerType firstPlayer, secondPlayer;

            firstPlayer = Player.ePlayerType.Mortal;
            secondPlayer = i_Settings.IsSinglePlayer ? Player.ePlayerType.AI : Player.ePlayerType.Mortal;

            m_Players[0] = new Player(i_Settings.Player1Name, firstPlayer);
            m_Players[1] = new Player(i_Settings.Player2Name, secondPlayer);
        }

        public void ResetPlayersScores()
        {
            foreach (Player currentPlayer in m_Players)
            {
                currentPlayer.Score = 0;
            }
        }
    }
}
